﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using Sinacor.Infra.UI.Controls.SmartClient;
using System.Collections;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Windows.Markup;
using System.Linq;
using System.Windows.Data;
using System.ComponentModel;

namespace Sinacor.Infra.UI.Controls.SmartClient
{
    public class SinacorExplorer : Control
    {
        public SinacorExplorer()
        {
            _views = new ExplorerViewCollection(this);
            _views.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_views_CollectionChanged);

            _viewDescriptions = new ObservableCollection<ExplorerViewDescription>();

            _readOnlyViewDescriptions = CollectionViewSource.GetDefaultView(new ReadOnlyObservableCollection<ExplorerViewDescription>(_viewDescriptions));
            _readOnlyViewDescriptions.SortDescriptions.Add(new SortDescription("SortIndex", ListSortDirection.Ascending));

            this.AddHandler(SinacorComboBox.SelectionChangedEvent, new SelectionChangedEventHandler(ViewDescriptionsComboBox_SelectionChanged));
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            SinacorComboBox viewDescriptionsComboBox = (SinacorComboBox)this.Template.FindName("PART_ViewDescriptionsComboBox", this);

            if (viewDescriptionsComboBox != null)
                viewDescriptionsComboBox.UpdateTarget(SinacorComboBox.SelectedValueProperty);
        }

        #region Groups

        #region Groups
        public static readonly DependencyProperty GroupsProperty = DependencyProperty.Register("Groups", typeof(object), typeof(SinacorExplorer), new FrameworkPropertyMetadata(null));

        public object Groups
        {
            get { return (object)GetValue(GroupsProperty); }
            set { SetValue(GroupsProperty, value); }
        }

        public static readonly DependencyProperty GroupsStringFormatProperty = DependencyProperty.Register("GroupsStringFormat", typeof(string), typeof(SinacorExplorer), new FrameworkPropertyMetadata(null));

        public string GroupsStringFormat
        {
            get { return (string)GetValue(GroupsStringFormatProperty); }
            set { SetValue(GroupsStringFormatProperty, value); }
        }

        public static readonly DependencyProperty GroupsTemplateProperty = DependencyProperty.Register("GroupsTemplate", typeof(DataTemplate), typeof(SinacorExplorer), new FrameworkPropertyMetadata(null));

        public DataTemplate GroupsTemplate
        {
            get { return (DataTemplate)GetValue(GroupsTemplateProperty); }
            set { SetValue(GroupsTemplateProperty, value); }
        }

        public static readonly DependencyProperty GroupsTemplateSelectorProperty = DependencyProperty.Register("GroupsTemplateSelector", typeof(DataTemplateSelector), typeof(SinacorExplorer), new FrameworkPropertyMetadata(null));

        public DataTemplateSelector GroupsTemplateSelector
        {
            get { return (DataTemplateSelector)GetValue(GroupsTemplateSelectorProperty); }
            set { SetValue(GroupsTemplateSelectorProperty, value); }
        }
        #endregion

        #region GroupsWidth
        public static readonly DependencyProperty GroupsWidthProperty = DependencyProperty.Register("GroupsWidth", typeof(GridLength), typeof(SinacorExplorer), new FrameworkPropertyMetadata(new GridLength(1, GridUnitType.Star), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        public GridLength GroupsWidth
        {
            get { return (GridLength)GetValue(GroupsWidthProperty); }
            set { SetValue(GroupsWidthProperty, value); }
        }

        public static readonly DependencyProperty GroupsMinWidthProperty = DependencyProperty.Register("GroupsMinWidth", typeof(double), typeof(SinacorExplorer), new FrameworkPropertyMetadata((double)0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        public double GroupsMinWidth
        {
            get { return (double)GetValue(GroupsMinWidthProperty); }
            set { SetValue(GroupsMinWidthProperty, value); }
        }

        public static readonly DependencyProperty GroupsMaxWidthProperty = DependencyProperty.Register("GroupsMaxWidth", typeof(double), typeof(SinacorExplorer), new FrameworkPropertyMetadata(System.Double.PositiveInfinity, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        public double GroupsMaxWidth
        {
            get { return (double)GetValue(GroupsMaxWidthProperty); }
            set { SetValue(GroupsMaxWidthProperty, value); }
        }
        #endregion

        #endregion

        #region NavigationPath
        public static readonly DependencyProperty NavigationPathProperty = DependencyProperty.Register("NavigationPath", typeof(object), typeof(SinacorExplorer), new FrameworkPropertyMetadata(null));

        public object NavigationPath
        {
            get { return (object)GetValue(NavigationPathProperty); }
            set { SetValue(NavigationPathProperty, value); }
        }

        public static readonly DependencyProperty NavigationPathStringFormatProperty = DependencyProperty.Register("NavigationPathStringFormat", typeof(string), typeof(SinacorExplorer), new FrameworkPropertyMetadata(null));

        public string NavigationPathStringFormat
        {
            get { return (string)GetValue(NavigationPathStringFormatProperty); }
            set { SetValue(NavigationPathStringFormatProperty, value); }
        }

        public static readonly DependencyProperty NavigationPathTemplateProperty = DependencyProperty.Register("NavigationPathTemplate", typeof(DataTemplate), typeof(SinacorExplorer), new FrameworkPropertyMetadata(null));

        public DataTemplate NavigationPathTemplate
        {
            get { return (DataTemplate)GetValue(NavigationPathTemplateProperty); }
            set { SetValue(NavigationPathTemplateProperty, value); }
        }

        public static readonly DependencyProperty NavigationPathTemplateSelectorProperty = DependencyProperty.Register("NavigationPathTemplateSelector", typeof(DataTemplateSelector), typeof(SinacorExplorer), new FrameworkPropertyMetadata(null));

        public DataTemplateSelector NavigationPathTemplateSelector
        {
            get { return (DataTemplateSelector)GetValue(NavigationPathTemplateSelectorProperty); }
            set { SetValue(NavigationPathTemplateSelectorProperty, value); }
        }
        #endregion

        #region Views

        #region Views
        private ExplorerViewCollection _views;

        public ExplorerViewCollection Views
        {
            get { return _views; }
        }

        private void _views_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            this.HasViews = this.Views.Count > 0;
            this.CanChangeSelectedView = this.Views.Count > 1;
        }

        private static readonly DependencyPropertyKey HasViewsKey = DependencyProperty.RegisterReadOnly("HasViews", typeof(bool), typeof(SinacorExplorer), new FrameworkPropertyMetadata(false));
        public static readonly DependencyProperty HasViewsProperty = HasViewsKey.DependencyProperty;

        public bool HasViews
        {
            get { return (bool)GetValue(HasViewsProperty); }
            private set { SetValue(HasViewsKey, value); }
        }
        #endregion

        #region ViewDescriptions
        private void ViewDescriptionsComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.OriginalSource is SinacorComboBox && ((SinacorComboBox)e.OriginalSource).Name == "PART_ViewDescriptionsComboBox")
            {
                e.Handled = true;
            }
        }

        private ObservableCollection<ExplorerViewDescription> _viewDescriptions;
        private ICollectionView _readOnlyViewDescriptions;

        public ICollectionView ViewDescriptions
        {
            get { return _readOnlyViewDescriptions; }
        }

        internal string GetFirstViewDescriptionName()
        {
            return this.GetFirstViewDescriptionName(null);
        }

        internal string GetFirstViewDescriptionName(ExplorerView view)
        {
            foreach (ExplorerViewDescription desc in this.ViewDescriptions)
            {
                if (view == null || desc.View == view)
                {
                    return desc.Name;
                }
            }

            return null;
        }

        internal void AddViewDescriptions(IEnumerable<ExplorerViewDescription> viewDescriptions)
        {
            foreach (ExplorerViewDescription item in viewDescriptions)
            {
                if (_viewDescriptions.Any(v => v.Name == item.Name))
                    throw new InvalidOperationException(string.Format("The view {0} already exists", item.Name));

                _viewDescriptions.Add(item);
            }
        }

        internal void RemoveViewDescriptions(IEnumerable<ExplorerViewDescription> viewDescriptions)
        {
            foreach (ExplorerViewDescription item in viewDescriptions)
                _viewDescriptions.Remove(item);
        }
        #endregion

        #region ViewsWidth
        public static readonly DependencyProperty ViewsWidthProperty = DependencyProperty.Register("ViewsWidth", typeof(GridLength), typeof(SinacorExplorer), new FrameworkPropertyMetadata(new GridLength(1, GridUnitType.Star), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        public GridLength ViewsWidth
        {
            get { return (GridLength)GetValue(ViewsWidthProperty); }
            set { SetValue(ViewsWidthProperty, value); }
        }

        public static readonly DependencyProperty ViewsMinWidthProperty = DependencyProperty.Register("ViewsMinWidth", typeof(double), typeof(SinacorExplorer), new FrameworkPropertyMetadata((double)0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        public double ViewsMinWidth
        {
            get { return (double)GetValue(ViewsMinWidthProperty); }
            set { SetValue(ViewsMinWidthProperty, value); }
        }

        public static readonly DependencyProperty ViewsMaxWidthProperty = DependencyProperty.Register("ViewsMaxWidth", typeof(double), typeof(SinacorExplorer), new FrameworkPropertyMetadata(System.Double.PositiveInfinity, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        public double ViewsMaxWidth
        {
            get { return (double)GetValue(ViewsMaxWidthProperty); }
            set { SetValue(ViewsMaxWidthProperty, value); }
        }
        #endregion

        #region ViewsHeight
        public static readonly DependencyProperty ViewsHeightProperty = DependencyProperty.Register("ViewsHeight", typeof(GridLength), typeof(SinacorExplorer), new FrameworkPropertyMetadata(new GridLength(1, GridUnitType.Star), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, new CoerceValueCallback(CoerceViewsHeight)));

        public GridLength ViewsHeight
        {
            get { return (GridLength)GetValue(ViewsHeightProperty); }
            set { SetValue(ViewsHeightProperty, value); }
        }

        private static object CoerceViewsHeight(DependencyObject d, object value)
        {
            SinacorExplorer explorer = (SinacorExplorer)d;

            if (!explorer.IsItemDetailsVisible)
                value = new GridLength(1, GridUnitType.Star);

            return value;
        }

        public static readonly DependencyProperty ViewsMinHeightProperty = DependencyProperty.Register("ViewsMinHeight", typeof(double), typeof(SinacorExplorer), new FrameworkPropertyMetadata((double)0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, new CoerceValueCallback(CoerceViewsMinHeight)));

        public double ViewsMinHeight
        {
            get { return (double)GetValue(ViewsMinHeightProperty); }
            set { SetValue(ViewsMinHeightProperty, value); }
        }

        private static object CoerceViewsMinHeight(DependencyObject d, object value)
        {
            SinacorExplorer explorer = (SinacorExplorer)d;

            if (!explorer.IsItemDetailsVisible)
                value = (double)0;

            return value;
        }

        public static readonly DependencyProperty ViewsMaxHeightProperty = DependencyProperty.Register("ViewsMaxHeight", typeof(double), typeof(SinacorExplorer), new FrameworkPropertyMetadata(System.Double.PositiveInfinity, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, new CoerceValueCallback(CoerceViewsMaxHeight)));

        public double ViewsMaxHeight
        {
            get { return (double)GetValue(ViewsMaxHeightProperty); }
            set { SetValue(ViewsMaxHeightProperty, value); }
        }

        private static object CoerceViewsMaxHeight(DependencyObject d, object value)
        {
            SinacorExplorer explorer = (SinacorExplorer)d;

            if (!explorer.IsItemDetailsVisible)
                value = System.Double.PositiveInfinity;

            return value;
        }
        #endregion

        #endregion

        #region ItemDetails

        #region ItemDetails
        public static readonly DependencyProperty ItemDetailsProperty = DependencyProperty.Register("ItemDetails", typeof(object), typeof(SinacorExplorer), new FrameworkPropertyMetadata(null));

        public object ItemDetails
        {
            get { return (object)GetValue(ItemDetailsProperty); }
            set { SetValue(ItemDetailsProperty, value); }
        }

        public static readonly DependencyProperty ItemDetailsStringFormatProperty = DependencyProperty.Register("ItemDetailsStringFormat", typeof(string), typeof(SinacorExplorer), new FrameworkPropertyMetadata(null));

        public string ItemDetailsStringFormat
        {
            get { return (string)GetValue(ItemDetailsStringFormatProperty); }
            set { SetValue(ItemDetailsStringFormatProperty, value); }
        }

        public static readonly DependencyProperty ItemDetailsTemplateProperty = DependencyProperty.Register("ItemDetailsTemplate", typeof(DataTemplate), typeof(SinacorExplorer), new FrameworkPropertyMetadata(null));

        public DataTemplate ItemDetailsTemplate
        {
            get { return (DataTemplate)GetValue(ItemDetailsTemplateProperty); }
            set { SetValue(ItemDetailsTemplateProperty, value); }
        }

        public static readonly DependencyProperty ItemDetailsTemplateSelectorProperty = DependencyProperty.Register("ItemDetailsTemplateSelector", typeof(DataTemplateSelector), typeof(SinacorExplorer), new FrameworkPropertyMetadata(null));

        public DataTemplateSelector ItemDetailsTemplateSelector
        {
            get { return (DataTemplateSelector)GetValue(ItemDetailsTemplateSelectorProperty); }
            set { SetValue(ItemDetailsTemplateSelectorProperty, value); }
        }
        #endregion

        #region IsItemDetailsVisible
        public static readonly DependencyProperty IsItemDetailsVisibleProperty = DependencyProperty.Register("IsItemDetailsVisible", typeof(bool), typeof(SinacorExplorer), new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnIsItemDetailsVisibleChanged)));

        public bool IsItemDetailsVisible
        {
            get { return (bool)GetValue(IsItemDetailsVisibleProperty); }
            set { SetValue(IsItemDetailsVisibleProperty, value); }
        }

        private static void OnIsItemDetailsVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SinacorExplorer explorer = (SinacorExplorer)d;

            explorer.CoerceValue(ViewsHeightProperty);
            explorer.CoerceValue(ViewsMinHeightProperty);
            explorer.CoerceValue(ViewsMaxHeightProperty);

            explorer.CoerceValue(ItemDetailsHeightProperty);
            explorer.CoerceValue(ItemDetailsMinHeightProperty);
            explorer.CoerceValue(ItemDetailsMaxHeightProperty);
        }
        #endregion

        #region ItemDetailsHeight
        public static readonly DependencyProperty ItemDetailsHeightProperty = DependencyProperty.Register("ItemDetailsHeight", typeof(GridLength), typeof(SinacorExplorer), new FrameworkPropertyMetadata(new GridLength(1, GridUnitType.Star), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, new CoerceValueCallback(CoerceItemDetailsHeight)));

        public GridLength ItemDetailsHeight
        {
            get { return (GridLength)GetValue(ItemDetailsHeightProperty); }
            set { SetValue(ItemDetailsHeightProperty, value); }
        }

        private static object CoerceItemDetailsHeight(DependencyObject d, object value)
        {
            SinacorExplorer explorer = (SinacorExplorer)d;

            if (!explorer.IsItemDetailsVisible)
                value = new GridLength(0, GridUnitType.Star);

            return value;
        }

        public static readonly DependencyProperty ItemDetailsMinHeightProperty = DependencyProperty.Register("ItemDetailsMinHeight", typeof(double), typeof(SinacorExplorer), new FrameworkPropertyMetadata((double)0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, new CoerceValueCallback(CoerceItemDetailsMinHeight)));

        public double ItemDetailsMinHeight
        {
            get { return (double)GetValue(ItemDetailsMinHeightProperty); }
            set { SetValue(ItemDetailsMinHeightProperty, value); }
        }

        private static object CoerceItemDetailsMinHeight(DependencyObject d, object value)
        {
            SinacorExplorer explorer = (SinacorExplorer)d;

            if (!explorer.IsItemDetailsVisible)
                value = (double)0;

            return value;
        }

        public static readonly DependencyProperty ItemDetailsMaxHeightProperty = DependencyProperty.Register("ItemDetailsMaxHeight", typeof(double), typeof(SinacorExplorer), new FrameworkPropertyMetadata(System.Double.PositiveInfinity, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, new CoerceValueCallback(CoerceItemDetailsMaxHeight)));

        public double ItemDetailsMaxHeight
        {
            get { return (double)GetValue(ItemDetailsMaxHeightProperty); }
            set { SetValue(ItemDetailsMaxHeightProperty, value); }
        }

        private static object CoerceItemDetailsMaxHeight(DependencyObject d, object value)
        {
            SinacorExplorer explorer = (SinacorExplorer)d;

            if (!explorer.IsItemDetailsVisible)
                value = System.Double.PositiveInfinity;

            return value;
        }
        #endregion

        #endregion

        #region SelectedView

        #region SelectedView
        public static readonly DependencyProperty SelectedViewProperty = DependencyProperty.Register("SelectedView", typeof(string), typeof(SinacorExplorer), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnSelectedViewChanged), new CoerceValueCallback(CoerceSelectedView)));

        public string SelectedView
        {
            get { return (string)GetValue(SelectedViewProperty); }
            set { SetValue(SelectedViewProperty, value); }
        }

        private static object CoerceSelectedView(DependencyObject d, object value)
        {
            SinacorExplorer explorer = (SinacorExplorer)d;

            if (explorer.Views[(string)value] == null)
                value = null;

            return value;
        }

        private static void OnSelectedViewChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SinacorExplorer explorer = (SinacorExplorer)d;

            ExplorerView oldView = e.OldValue != null ? explorer.Views[(string)e.OldValue] : null;
            ExplorerView newView = e.NewValue != null ? explorer.Views[(string)e.NewValue] : null;

            if (oldView != null)
                oldView.Visibility = Visibility.Collapsed;

            if (newView != null)
                newView.Visibility = Visibility.Visible;

            explorer.OnSelectedViewChanged((string)e.OldValue, (string)e.NewValue);
        }

        private static readonly DependencyPropertyKey CanChangeSelectedViewKey = DependencyProperty.RegisterReadOnly("CanChangeSelectedView", typeof(bool), typeof(SinacorExplorer), new FrameworkPropertyMetadata(false));
        public static readonly DependencyProperty CanChangeSelectedViewProperty = CanChangeSelectedViewKey.DependencyProperty;

        public bool CanChangeSelectedView
        {
            get { return (bool)GetValue(CanChangeSelectedViewProperty); }
            private set { SetValue(CanChangeSelectedViewKey, value); }
        }
        #endregion

        #region SelectedViewChanged
        public static readonly RoutedEvent SelectedViewChangedEvent = EventManager.RegisterRoutedEvent("SelectedViewChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<string>), typeof(SinacorExplorer));

        public event RoutedPropertyChangedEventHandler<string> SelectedViewChanged
        {
            add { AddHandler(SelectedViewChangedEvent, value); }
            remove { RemoveHandler(SelectedViewChangedEvent, value); }
        }

        protected virtual void OnSelectedViewChanged(string oldView, string newView)
        {
            RoutedPropertyChangedEventArgs<string> e = new RoutedPropertyChangedEventArgs<string>(oldView, newView, SelectedViewChangedEvent);
            this.RaiseEvent(e);
        }
        #endregion

        #endregion
    }
}